/* Copyright (C) 2014 InfiniDB, Inc.
   Copyright (C) 2016-2022 MariaDB Corporation
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; version 2 of
   the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

/******************************************************************************
 * $Id: brmtypes.h 1956 2013-08-09 22:39:36Z wweeks $
 *
 *****************************************************************************/

/** @file
 */

#pragma once

#include <vector>
#include <sys/types.h>
#include <climits>
#include <string>
#include <ctime>
#include "mcs_basic_types.h"
#include "logicalpartition.h"

#include <tr1/unordered_map>
#ifndef _UNORDERED_MAP_FIX_
#define _UNORDERED_MAP_FIX_
#endif  //_UNORDERED_MAP_FIX_

#include "calpontsystemcatalog.h"
#include "bytestream.h"
#include "messageobj.h"
#include "messagelog.h"
#include "loggingid.h"

#define EXPORT

namespace idbdatafile
{
class IDBDataFile;
}

namespace BRM
{
/* these types should be defined in the system catalog header */
typedef int64_t LBID_t;  /// The LBID space is currently 36 bits.  We use 64 here.
typedef uint32_t HWM_t;
typedef int32_t VER_t;
/// Object ID type.  Currently a 32-bit number.  24 for Object number, 8 for partition number.
typedef execplan::CalpontSystemCatalog::OID OID_t;

typedef std::pair<LBID_t, VER_t> LVP_t;

typedef std::vector<LVP_t> BlockList_t;

/* LBIDRange and VBRange used to be simple structs and were converted to classes
to inherit Serializeable, hence the all-public definitions */

/// The InlineLBIDRange struct is for use internally by the ExtentMap
struct InlineLBIDRange
{
  LBID_t start;
  uint32_t size;
#ifndef __LP64__
  int32_t pad1;
#endif
};

#define MAX_PROCNAME 16

/** @brief SID = Session ID */
typedef uint32_t SID;
/** @brief A type describing a single transaction ID */
struct _TxnID
{
  /// The TransactionID number
  execplan::CalpontSystemCatalog::SCN id;
  /// True iff the id is valid.
  bool valid;
  EXPORT _TxnID();
};
/** @brief A type describing a single transaction ID */
typedef struct _TxnID TxnID;

/** @brief A type associating a session with a transaction.
 */
struct _SIDTIDEntry
{
  /// The Transaction ID.  txnid.valid determines whether or not this SIDTIDEntry is valid
  TxnID txnid;
  /// The session doing the transaction
  SID sessionid;
  EXPORT _SIDTIDEntry();
  EXPORT void init();
};
/** @brief A type associating a session with a transaction */
typedef struct _SIDTIDEntry SIDTIDEntry;

// @bug 1970 - Added CPInfo and CPMaxMin structs used by new interface that allows setting the max and min CP
// data for multiple extents.

// Special seqNum field values.
#define SEQNUM_MARK_INVALID (-1)
#define SEQNUM_MARK_INVALID_SET_RANGE (-2)
#define SEQNUM_MARK_UPDATING_INVALID_SET_RANGE (-3)

// Used in vectors.
struct CPInfo
{
  LBID_t firstLbid;
  int64_t max;
  int64_t min;
  int32_t seqNum;
  union
  {
    int128_t bigMax;
    int64_t max_;
  };
  union
  {
    int128_t bigMin;
    int64_t min_;
  };
  bool isBinaryColumn;  // XXX: we should remove these two fields and replace it with type handler.
};
typedef std::vector<CPInfo> CPInfoList_t;

// Used for map where lbid is the key.
struct CPMaxMin
{
  int64_t max;
  int64_t min;
  int32_t seqNum;
  union
  {
    int128_t bigMax;
    int64_t max_;
  };
  union
  {
    int128_t bigMin;
    int64_t min_;
  };
  bool isBinaryColumn;  // XXX: these two fields should be replaced with type handler pointer.
};
typedef std::tr1::unordered_map<LBID_t, CPMaxMin> CPMaxMinMap_t;

// @bug 2117 - Add second CP struct to use in merging CP info

struct CPInfoMerge
{
  LBID_t startLbid;  // starting LBID for relevant extent
  int64_t max;       // max value to be merged with current max value
  int64_t min;       // min value to be merged with current min value
  int32_t seqNum;    // sequence number (not currently used)
  execplan::CalpontSystemCatalog::ColDataType type;
  int32_t colWidth;
  bool newExtent;  // is this to be treated as a new extent
  union
  {
    int128_t bigMax;
    int64_t max_;
  };
  union
  {
    int128_t bigMin;
    int64_t min_;
  };
};
typedef std::vector<CPInfoMerge> CPInfoMergeList_t;

// Used for map where lbid is the key.  Data members have same meaning as
// those in CPInfoMerge.
struct CPMaxMinMerge
{
  int64_t max;
  int64_t min;
  int32_t seqNum;
  execplan::CalpontSystemCatalog::ColDataType type;
  int32_t colWidth;
  bool newExtent;
  union
  {
    int128_t bigMax;
    int64_t max_;
  };
  union
  {
    int128_t bigMin;
    int64_t min_;
  };
};
typedef std::tr1::unordered_map<LBID_t, CPMaxMinMerge> CPMaxMinMergeMap_t;

// end of bug 2117

typedef std::tr1::unordered_map<execplan::CalpontSystemCatalog::OID, execplan::CalpontSystemCatalog::OID>
    OidsMap_t;

struct ExtentInfo
{
  execplan::CalpontSystemCatalog::OID oid;
  uint32_t partitionNum;  // starts at 0
  uint16_t segmentNum;    // starts at 0
  uint16_t dbRoot;        // starts at 1 to match Columnstore.xml
  HWM_t hwm;
  bool newFile;
};

struct FileInfo
{
  execplan::CalpontSystemCatalog::OID oid;
  uint32_t partitionNum;  // starts at 0
  uint16_t segmentNum;    // starts at 0
  uint16_t dbRoot;        // starts at 1 to match Columnstore.xml
  uint16_t compType;      // compression type
};

typedef std::tr1::unordered_map<execplan::CalpontSystemCatalog::OID, ExtentInfo> ExtentsInfoMap_t;

enum LockState
{
  LOADING,
  CLEANUP
};

struct TableLockInfo : public messageqcpp::Serializeable
{
  uint64_t id;
  uint32_t tableOID;
  std::string ownerName;
  uint32_t ownerPID;
  int32_t ownerSessionID;
  int32_t ownerTxnID;
  LockState state;
  time_t creationTime;
  std::vector<uint32_t> dbrootList;

  bool overlaps(const TableLockInfo&, const std::set<uint32_t>& sPMList) const;
  EXPORT void serialize(messageqcpp::ByteStream& bs) const override;
  EXPORT void serialize(std::ostream&) const;
  EXPORT void deserialize(std::istream&);
  EXPORT void deserialize(messageqcpp::ByteStream& bs) override;
  EXPORT void serialize(idbdatafile::IDBDataFile*) const;
  EXPORT void deserialize(idbdatafile::IDBDataFile*);
  EXPORT void serialize(char* buffer, uint32_t& offset);
  EXPORT uint32_t getInternalSize() const;
  bool operator<(const TableLockInfo&) const;

 private:
  void serializeElement(char* buffer, const char* src, uint32_t size, uint32_t& offset);
};

/// A Serializeable version of InlineLBIDRange
class LBIDRange : public messageqcpp::Serializeable
{
 public:
  LBID_t start;
  uint32_t size;

  EXPORT LBIDRange();
  LBIDRange(const LBID_t aStart, const uint32_t aSize) : start(aStart), size(aSize)
  {
  }
  EXPORT LBIDRange(const LBIDRange& l);
  EXPORT explicit LBIDRange(const InlineLBIDRange& l);
  EXPORT LBIDRange& operator=(const LBIDRange& l);
  EXPORT LBIDRange& operator=(const InlineLBIDRange& l);
  EXPORT ~LBIDRange() override;

  /** The Serializeable interface.  Exports the instance to the bytestream */
  EXPORT void serialize(messageqcpp::ByteStream& bs) const override;
  /** The Serializeable interface.  Initializes itself from the bytestrem. */
  EXPORT void deserialize(messageqcpp::ByteStream& bs) override;
};

/* To support bulkVSSLookup() */
struct VSSData
{
  VER_t verID;
  bool vbFlag;
  int returnCode;
};

/* Arg type for DBRM::bulkSetHWM() */
struct BulkSetHWMArg
{
  OID_t oid;
  uint32_t partNum;
  uint16_t segNum;
  HWM_t hwm;
};

/* Arg type for DBRM::bulkUpdateDBRoot() */
struct BulkUpdateDBRootArg
{
  LBID_t startLBID;  // starting LBID for the extent to update
  uint16_t dbRoot;   // the new dbRoot

  inline bool operator<(const BulkUpdateDBRootArg& b) const
  {
    return startLBID < b.startLBID;
  }
  explicit BulkUpdateDBRootArg(LBID_t l = 0, uint16_t d = 0) : startLBID(l), dbRoot(d)
  {
  }
};

/* Input Arg type for DBRM::createStripeColumnExtents() */
struct CreateStripeColumnExtentsArgIn
{
  OID_t oid = 0;       // column OID
  uint32_t width = 0;  // column width in bytes
  execplan::CalpontSystemCatalog::ColDataType colDataType;
};

/* Output Arg type for DBRM:createStripeColumnExtents() */
struct CreateStripeColumnExtentsArgOut
{
  LBID_t startLbid;         // starting LBID of allocated extent
  int allocSize;            // number of blocks in allocated extent
  uint32_t startBlkOffset;  // starting file block offset for allocated extent
};

/// A container for LBIDRanges
typedef std::vector<LBIDRange> LBIDRange_v;

/// Describes a contiguous range of blocks in the Version Buffer
class VBRange : public messageqcpp::Serializeable
{
 public:
  OID_t vbOID;
  uint32_t vbFBO;
  uint32_t size;

  EXPORT VBRange();
  EXPORT VBRange(const VBRange& v);
  EXPORT VBRange& operator=(const VBRange& v);
  EXPORT ~VBRange() override;
  EXPORT void serialize(messageqcpp::ByteStream& bs) const override;
  EXPORT void deserialize(messageqcpp::ByteStream& bs) override;
};

// Structure used to return HWM information for each DbRoot in a PM
struct EmDbRootHWMInfo
{
  uint32_t partitionNum;  // last partition in dbRoot
  uint16_t dbRoot;        // applicable dbRoot
  uint16_t segmentNum;    // last segment file in dbRoot
  HWM_t localHWM;         // local HWM in last file for this dbRoot
  uint32_t fbo;           // starting block offset to HWM extent
  LBID_t startLbid;       // starting LBID for HWM extent
  uint64_t totalBlocks;   // cumulative non-outOfService blks for this dbRoot.
  //   0 block count means no extents in this dbRoot,
  //   unless status is OutOfService; in which case
  //   the dbRoot has blocks that are all OutOfService
  int hwmExtentIndex;  // Internal use (idx to HWM extent in extent map)
  int16_t status;      // Avail, unAvail, outOfService
  EmDbRootHWMInfo()
  {
    init(0);
  }
  explicit EmDbRootHWMInfo(uint16_t root)
  {
    init(root);
  }
  void init(uint16_t root)
  {
    partitionNum = 0;
    dbRoot = root;
    segmentNum = 0;
    localHWM = 0;
    fbo = 0;
    startLbid = 0;
    hwmExtentIndex = -1;
    totalBlocks = 0;
    status = 0;
  }
};

typedef std::vector<EmDbRootHWMInfo> EmDbRootHWMInfo_v;

/// A container for VBRanges
typedef std::vector<VBRange> VBRange_v;

/* Definitions to support 'undo' operations */

#define ID_MAXSIZE 200

/** @brief ImageDeltas describe how an image in memory changed.
 *
 * ImageDelta objects contain the data that occupied the space at 'start'
 * before the last write operation was performed.  To reverse the change,
 * memcpy(ImageDelta.start, ImageDelta.data, ImageDelta.size).  The
 * shared memory segments should not be unlinked or unlocked since the
 * write operation.
 *
 * Right now it is used specifically to record how BRM data is modified.
 * Usage can be generalized.
 */
struct ImageDelta
{
  void* start;
  int size;
  char data[ID_MAXSIZE];  /// Has to be as large as the largest change
};

// SubSystemLogId enumeration values should be in sync with SubsystemID[]
// that is defined in messagelog.cpp
enum SubSystemLogId
{
  SubSystemLogId_controllerNode = 29,
  SubSystemLogId_workerNode = 30
};
EXPORT void logInit(SubSystemLogId subSystemId);
EXPORT void log(const std::string& msg, logging::LOG_TYPE = logging::LOG_TYPE_CRITICAL);
EXPORT void log_errno(const std::string& msg, logging::LOG_TYPE = logging::LOG_TYPE_CRITICAL);
EXPORT void errString(int rc, std::string& errMsg);

const struct timespec FIVE_MIN_TIMEOUT = {300, 0};

/* Function identifiers used for master-slave communication.

        The format of the messages from DBRM to Master and from Master to Slaves
        will look like
                <fcnID, in-params>
        The format of the messages from the Slaves to Master and Master to DBRM
        will look like
                <error code, out-params>
*/
// FIXME: put these in a enum. The current arrangement is error-prone.
const uint8_t DELETE_OID = 1;
const uint8_t WRITE_VB_ENTRY = 3;
const uint8_t BEGIN_VB_COPY = 4;
const uint8_t END_VB_COPY = 5;
const uint8_t VB_ROLLBACK1 = 6;
const uint8_t VB_ROLLBACK2 = 7;
const uint8_t VB_COMMIT = 8;
const uint8_t BRM_UNDO = 9;
const uint8_t CONFIRM = 10;
const uint8_t HALT = 11;
const uint8_t RESUME = 12;
const uint8_t RELOAD = 13;
const uint8_t SETREADONLY = 14;
const uint8_t SETREADWRITE = 15;
const uint8_t FLUSH_INODE_CACHES = 16;
const uint8_t BRM_CLEAR = 17;
const uint8_t MARKEXTENTINVALID = 18;
const uint8_t MARKMANYEXTENTSINVALID = 19;
const uint8_t GETREADONLY = 20;
const uint8_t SETEXTENTMAXMIN = 21;
const uint8_t DELETE_EMPTY_COL_EXTENTS = 24;
const uint8_t DELETE_EMPTY_DICT_STORE_EXTENTS = 25;
const uint8_t SETMANYEXTENTSMAXMIN = 26;
const uint8_t CREATE_DICT_STORE_EXTENT = 28;
const uint8_t SET_LOCAL_HWM = 29;
const uint8_t DELETE_OIDS = 30;
const uint8_t TAKE_SNAPSHOT = 31;
const uint8_t MERGEMANYEXTENTSMAXMIN = 32;
const uint8_t DELETE_PARTITION = 33;
const uint8_t MARK_PARTITION_FOR_DELETION = 34;
const uint8_t RESTORE_PARTITION = 35;
const uint8_t CREATE_COLUMN_EXTENT_DBROOT = 36;  // @bug 4091: To be deprecated
const uint8_t BULK_SET_HWM = 37;
const uint8_t ROLLBACK_COLUMN_EXTENTS_DBROOT = 38;
const uint8_t ROLLBACK_DICT_STORE_EXTENTS_DBROOT = 39;
const uint8_t BULK_SET_HWM_AND_CP = 40;
const uint8_t MARK_ALL_PARTITION_FOR_DELETION = 41;
const uint8_t CREATE_COLUMN_EXTENT_EXACT_FILE = 42;
const uint8_t DELETE_DBROOT = 43;
const uint8_t CREATE_STRIPE_COLUMN_EXTENTS = 44;

/* SessionManager interface */
const uint8_t VER_ID = 45;
const uint8_t NEW_TXN_ID = 46;
const uint8_t COMMITTED = 47;
const uint8_t ROLLED_BACK = 48;
const uint8_t GET_TXN_ID = 49;
const uint8_t SID_TID_MAP = 50;
const uint8_t SM_RESET = 51;
const uint8_t GET_UNIQUE_UINT32 = 52;
const uint8_t SYSCAT_VER_ID = 53;
const uint8_t GET_SYSTEM_STATE = 54;
const uint8_t SET_SYSTEM_STATE = 55;
const uint8_t GET_UNIQUE_UINT64 = 56;
const uint8_t CLEAR_SYSTEM_STATE = 57;
const uint8_t GET_UNCOMMITTED_LBIDS = 58;

/* OID Manager interface */
const uint8_t ALLOC_OIDS = 60;
const uint8_t RETURN_OIDS = 61;
const uint8_t OIDM_SIZE = 62;
const uint8_t ALLOC_VBOID = 63;
const uint8_t GETDBROOTOFVBOID = 64;
const uint8_t GETVBOIDTODBROOTMAP = 65;

/* New Table lock interface */
const uint8_t GET_TABLE_LOCK = 70;
const uint8_t RELEASE_TABLE_LOCK = 71;
const uint8_t CHANGE_TABLE_LOCK_STATE = 72;
const uint8_t CHANGE_TABLE_LOCK_OWNER = 73;
const uint8_t GET_ALL_TABLE_LOCKS = 74;
const uint8_t RELEASE_ALL_TABLE_LOCKS = 75;
const uint8_t GET_TABLE_LOCK_INFO = 76;
const uint8_t OWNER_CHECK = 77;  // the msg from the controller to worker

/* Autoincrement interface (WIP) */
const uint8_t START_AI_SEQUENCE = 80;
const uint8_t GET_AI_RANGE = 81;
const uint8_t RESET_AI_SEQUENCE = 82;
const uint8_t GET_AI_LOCK = 83;
const uint8_t RELEASE_AI_LOCK = 84;
const uint8_t DELETE_AI_SEQUENCE = 85;

/* Copylock interface */
const uint8_t LOCK_LBID_RANGES = 90;
const uint8_t RELEASE_LBID_RANGES = 91;

/* More main BRM functions 100-110 */
const uint8_t BULK_UPDATE_DBROOT = 100;
const uint8_t GET_SYSTEM_CATALOG = 101;
const uint8_t BULK_WRITE_VB_ENTRY = 102;

const uint8_t NEW_CPIMPORT_JOB = 103;
const uint8_t FINISH_CPIMPORT_JOB = 104;
const uint8_t START_READONLY = 105;
const uint8_t FORCE_CLEAR_CPIMPORT_JOBS = 106;

/* Error codes returned by the DBRM functions. */
/// The operation was successful
const int8_t ERR_OK = 0;

/// There was some unspecific failure and if the operation was a "write", no change was made
const int8_t ERR_FAILURE = 1;

/// The operation failed because at least one slave has a different image than the others.  No change was
/// made.
const int8_t ERR_SLAVE_INCONSISTENCY = 2;

/// The operation failed because of a communication problem.
const int8_t ERR_NETWORK = 3;

/// The operation failed because one slave did not send a response within 10 seconds.  The administrator
/// should look into it.
const int8_t ERR_TIMEOUT = 4;

/// The operation failed because the Master is in read-only mode.  Either it detected an serious error (ie
/// ERR_SLAVE_INCONSISTENCY), or the administrator set it using dbrmctl.  The system needs the administrator
/// to fix the problem and enable read-write mode.
const int8_t ERR_READONLY = 5;

/// beginVBCopy was attempted, but deadlocked and was cancelled.  The transaction must be rolled back after
/// receiving this error code.
const int8_t ERR_DEADLOCK = 6;

/// While waiting for beginVBCopy to allocate the requested LBIDs, the transaction was killed by another
/// thread.  The caller must roll back after receiving this error code.
const int8_t ERR_KILLED = 7;

/// version buffer overflow error
const int8_t ERR_VBBM_OVERFLOW = 8;

const int8_t ERR_TABLE_LOCKED_ALREADY = 9;

/// Invalid operation against last partition in a table column
const int8_t ERR_INVALID_OP_LAST_PARTITION = 10;
const int8_t ERR_PARTITION_DISABLED = 11;
const int8_t ERR_NOT_EXIST_PARTITION = 12;
const int8_t ERR_PARTITION_ENABLED = 13;
const int8_t ERR_TABLE_NOT_LOCKED = 14;
const int8_t ERR_SNAPSHOT_TOO_OLD = 15;
const int8_t ERR_NO_PARTITION_PERFORMED = 16;

/// This error code is returned by writeVBEntry when a session with a low txnid attempts to write to a block
/// with a higher verid
const int8_t ERR_OLDTXN_OVERWRITING_NEWTXN = 17;

// structure used to hold the information to identify a partition for shared-nothing
struct PartitionInfo
{
  LogicalPartition lp;
  OID_t oid;

  void serialize(messageqcpp::ByteStream& b) const
  {
    lp.serialize(b);
    b << (uint32_t)oid;
  }

  void unserialize(messageqcpp::ByteStream& b)
  {
    lp.unserialize(b);
    b >> (uint32_t&)oid;
  }
};

// Note: Copies share the currentTxns array
class QueryContext : public messageqcpp::Serializeable
{
 public:
  explicit QueryContext(VER_t scn = 0) : currentScn(scn)
  {
    currentTxns.reset(new std::vector<VER_t>());
  }

  void serialize(messageqcpp::ByteStream& bs) const override
  {
    bs << currentScn;
    serializeInlineVector(bs, *currentTxns);
  }

  void deserialize(messageqcpp::ByteStream& bs) override
  {
    bs >> currentScn;
    deserializeInlineVector(bs, *currentTxns);
  }

  execplan::CalpontSystemCatalog::SCN currentScn;
  boost::shared_ptr<std::vector<execplan::CalpontSystemCatalog::SCN> > currentTxns;

 private:
  // defaults okay?
  // QueryContext(const QueryContext& rhs);
  // QueryContext& operator=(const QueryContext& rhs);
};

std::ostream& operator<<(std::ostream&, const QueryContext&);

}  // namespace BRM

#undef EXPORT
